package net.sevear.chaos.p2p
{
	import com.douban.auth.DBAuthenticate;
	import com.douban.media.DBRadio;
	
	import flash.events.EventDispatcher;
	import flash.events.NetStatusEvent;
	import flash.net.GroupSpecifier;
	import flash.net.NetConnection;
	import flash.net.NetGroup;
	import flash.net.SharedObject;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;
	
	import mx.controls.List;
	
	import net.sevear.chaos.download.Predef;
	import net.sevear.chaos.event.ChaosErrorEvent;
	import net.sevear.chaos.event.p2p.P2pEvent;
	import net.sevear.chaos.recommend.ChaosRemoteRequest;
	import net.sevear.chaos.utils.DataUtil;
	import net.sevear.chaos.utils.Logger;
	import net.sevear.chaos.vo.P2pSiteMsg;
	import net.sevear.chaos.vo.TrackInfo;
	import net.sevear.chaos.vo.UserInfo;

	public class P2PFMSiteServer extends EventDispatcher
	{
		private const CHAOS_P2PFM_URI:String = "/p2p/";
		
		private const SERVER:String = "rtmfp://p2p.rtmfp.net/fa5721529849aeba70c6cf56-b983c5cc6bbb/";
		private const DEVKEY:String = "fa5721529849aeba70c6cf56-b983c5cc6bbb";
		
		private var _owner:UserInfo;
		private var _groupName:String;
		
		private var _nc:NetConnection;
		private var _netGroup:NetGroup;
		private var _nearID:String;
		private var groupSpecWithAuthorizations:String;
		private var _connected:Boolean 	= false;
		private var _started:Boolean 	= false;
		private var _curTrack:TrackInfo;
		private var _listenerTotal:int;
		private var _listenerDic:Dictionary;
		private var so:SharedObject;
		private var autoOpen:Boolean = true;
		public function P2PFMSiteServer(uid:int)
		{
			super(null);
			initSO();
			_groupName = "net.sevear.chaos.fm"+uid;
			_listenerTotal = 0;
			_listenerDic = new Dictionary();
		}
		
		private static var instance:P2PFMSiteServer;
		public static function getInstance():P2PFMSiteServer{
			if(instance==null){
				instance = new P2PFMSiteServer(DBAuthenticate.getInstance().dbUID);
			}
			return instance;
		}
		
		private function initSO():void{
			so = SharedObject.getLocal("douchaos","/");
			
			if(so.data.autoOpenServer!=null){
//				autoOpen = (so.data.autoOpenServer as String) == "true";
				autoOpen = true;
			}else{
				so.data.autoOpenServer = "true";
				so.flush();
			}
			
			if(autoOpen==true && started == false){
				open();
			}
		}
		
		public function open():void{
			_nc = new NetConnection();
			_nc.addEventListener(NetStatusEvent.NET_STATUS,ncNetStatus);
			_nc.connect(SERVER+DEVKEY);
			so.data.autoOpenServer = "true";
			so.flush();
		}
		
		public function close():void{
			if(_connected){
				so.data.autoOpenServer = "false";
				so.flush();
				
				var msg:Object = new Object();
				msg.peerID = _nearID;
				msg.type = P2pSiteMsg.SITE_CLOSED;
				postMsg(msg)
				
				setTimeout(closeSite,1000);
			}
		}
		
		private function closeSite():void{
			_netGroup.close();
			_listenerTotal = 0;
			_connected = false;
			_started = true;
			_listenerDic = new Dictionary();
			dispatchEvent(new P2pEvent(P2pEvent.CLOSED_SITE));
		}
		
		public function broadCastTrack(track:TrackInfo):void{
			if(_started && _connected){
				if(_curTrack == null || _curTrack.sid != track.sid){
					_curTrack = track;
					var msg:Object = new Object();
					msg.peerID = _nearID;
					msg.data = track;
					msg.type = P2pSiteMsg.NEW_TRACK;
					postMsg(msg);
					_likeCount = 0;
					dispatchEvent(new P2pEvent(P2pEvent.LISTENER_LIKE_TRACK));
				}
			}
		}
		
		public function get connected():Boolean{
			return _connected;
		}
		
		public function get started():Boolean{
			return _started;
		}
		
		public function get listenerTotal():int{
			if(started){
				return Math.max(0,_netGroup.estimatedMemberCount-1);
			}else{
				return 0;
			}
		}
		
		private var _likeCount:int;
		public function get listenerLikeCount():int{
			return _likeCount;
		}
		
		private function ncNetStatus(e:NetStatusEvent):void{
			switch(e.info.code)
			{
				case "NetConnection.Connect.Success":
					_nearID = e.target.nearID as String;// or you can use event.target.nearID;
					setupGroup();
					break;
				case "NetGroup.Connect.Success":
					_connected = true;
					_started = true;
					reportSiteOpenToServer();
					broadcastSiteOpen();
					Logger.getInstance().log("P2p Server has started, id:",_nc.	farNonce,_nc.farID);
					dispatchEvent(new P2pEvent(P2pEvent.START_SITE_SUCCESSFUL));
					break;
				case "NetGroup.Connect.Failed":
				case "NetGroup.Connect.Rejected":
					_connected = false;
					dispatchEvent(new P2pEvent(P2pEvent.START_SITE_FAILED));
					break;
				case "NetGroup.Posting.Notify":
					receivePostNotifyMessage(e.info.message);
					break;
				// FYI: More NetGroup event info codes
				case "NetGroup.Neighbor.Connect":
					//TODO:
					break;
				case "NetGroup.LocalCoverage.Notify":
				case "NetGroup.SendTo.Notify": // event.info.message, event.info.from, event.info.fromLocal
				case "NetGroup.MulticastStream.PublishNotify": // event.info.name
				case "NetGroup.MulticastStream.UnpublishNotify": // event.info.name
				case "NetGroup.Replication.Fetch.SendNotify": // event.info.index
				case "NetGroup.Replication.Fetch.Failed": // event.info.index
				case "NetGroup.Replication.Fetch.Result": // event.info.index, event.info.object
				case "NetGroup.Replication.Request": // event.info.index, event.info.requestID
				default:
				{
					break;
				}
			}
		}
		
		/**
		 * Connect with the NETGROUP
		 * */
		private function setupGroup():void
		{
			var groupspec:GroupSpecifier = new GroupSpecifier(_groupName);
			// Allow group members to open channels to server
			groupspec.serverChannelEnabled = true;
			// Allow group members to post
			groupspec.postingEnabled = true;
			
			// Create the group specifi
			groupSpecWithAuthorizations = groupspec.groupspecWithAuthorizations();
			
			// Join the group specified by groupspec
			_netGroup = new NetGroup(_nc, groupSpecWithAuthorizations);
			
			// Register listener for NetGroup NetStatus events
			_netGroup.addEventListener(NetStatusEvent.NET_STATUS, ncNetStatus);
		}
		
		private var _msgOutCount:int = 0;
		private function postMsg(msgObj:Object):void{
			msgObj.sequence = ++ _msgOutCount;
			_netGroup.post(msgObj);
		}
		
		private function directSendMsg(msgObj:Object,peerID:String):void{
			msgObj.sequence = ++ _msgOutCount;
		}
		
		private function receivePostNotifyMessage(netGroupMessage:Object):void
		{
			var msg:Object = netGroupMessage as Object;
			switch(msg.type){
				case P2pSiteMsg.LISTENER_WAIT_TO_CONNECT:
					addListener(msg.peerID as String,msg.data);
					reBroadCastCurTrack();
					break;
				case P2pSiteMsg.LISTENER_DISCONNECT:
					removeListener(msg.peerID  as String);
					break;
				case P2pSiteMsg.REQUEST_CUR_TRACK:
					broadCastTrack(DBRadio.getInstance().curSong);
					break;
				case P2pSiteMsg.LIKE_TRACK:
					onListenerLikeTrack(msg.trackID as int);
					break;
			}
		}
		
		private function onListenerLikeTrack(trackID:int):void{
			if(_curTrack.sid == trackID){
				_likeCount ++;
				dispatchEvent(new P2pEvent(P2pEvent.LISTENER_LIKE_TRACK,_likeCount));
			}
			
		}
		
		private function reBroadCastCurTrack():void{
			if(_curTrack!=null){
				var msg:Object = new Object();
				msg.peerID = _nearID;
				msg.data = _curTrack
				msg.type = P2pSiteMsg.NEW_TRACK;
				postMsg(msg);
			}
		}
		
		private function addListener(peerID:String,userInfo:Object):void{
			if(userInfo!=null){
				if(_listenerDic[peerID]==null){
					if(_netGroup.addNeighbor(peerID)){
						var newUser:UserInfo = new UserInfo();
						DataUtil.parseP2pUserInfo(userInfo,newUser);
						_listenerDic[peerID] = newUser;
						_listenerTotal++;
						sendSiteInfoMsg(P2pSiteMsg.SITE_ADDED_LISTENER);
						dispatchEvent(new P2pEvent(P2pEvent.NEW_LISTENER_ADDED,userInfo));
						
					}else{
						dispatchEvent(new P2pEvent(P2pEvent.ADD_LISTENER_FAILED,userInfo));
					}
				}
			}
		}
		
		private function broadcastSiteOpen():void{
			sendSiteInfoMsg(P2pSiteMsg.SITE_STARTUP);
		}
		
		private var reportRst:ChaosRemoteRequest;
		private function reportSiteOpenToServer():void{
			reportRst = new ChaosRemoteRequest(3);
			reportRst.addEventListener(ChaosErrorEvent.CHAOS_ERR,onReportErr);
			var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+CHAOS_P2PFM_URI));
			rst.method = URLRequestMethod.POST;
			var vars:URLVariables = new URLVariables();
			vars.op = "open";
			vars.id = DBAuthenticate.getInstance().dbUID;
			rst.data = vars;
			reportRst.sendRequest(rst,null);
		}
		
		private function sendSiteInfoMsg(type:int):void{
			var msg:Object = new Object();
			msg.peerID = _nearID;
			msg.type = type;
			var userInfo:UserInfo = new UserInfo();
			var authenicate:DBAuthenticate = DBAuthenticate.getInstance();
			userInfo.dbID = authenicate.dbUID;
			userInfo.dbUID = authenicate.dbScreenName;
			userInfo.dbName = authenicate.dbName;
			userInfo.dbURL = authenicate.userUrl;
			userInfo.imgUrl = authenicate.imgURL;
			userInfo.isActivate = UserInfo.ACTIVATE_CHAOS;
			msg.data = userInfo;
			postMsg(msg);
		}
		
		private function removeListener(peerID:String):void{
			if(_listenerDic[peerID]!=null){
				_listenerTotal--;
				dispatchEvent(new P2pEvent(P2pEvent.LISTENER_DISCONNECTED,_listenerDic[peerID]));
				_listenerDic[peerID] = null;
			}
		}
		
		//TODO:
		private function onReportErr(e:ChaosErrorEvent):void{
			
		}
	}
}